package com.metaverse.backend.service;

import com.metaverse.backend.dto.gen.GenCode;
import com.metaverse.backend.dto.gen.TableField;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.geom.AffineTransform;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class GenCodeService {
    @Autowired
    private Configuration cfg;

    public void genController(GenCode model) throws IOException, TemplateException {

        Path targetFile = StringUtils.isNotBlank(model.getGenPackage()) ?
                Paths.get(model.getJavaPath(), "web", model.getGenPackage(), model.getClassName() + "Controller.java")
                        .toAbsolutePath() :
                Paths.get(model.getJavaPath(), "web", model.getClassName() + "Controller.java").toAbsolutePath();
        Map<String, Object> extra = new HashMap<>();
        if (StringUtil.isNotEmpty(model.getGenPackage())) {
            List<String> imports = new ArrayList<>();
            imports.add("import " + model.getBasePackage() + ".web.BaseController;");
            extra.put("imports", imports);
        }
        extra.put("subPackage", StringUtil.isNotEmpty(model.getGenPackage()));
        extra.put("softDelete", canSoftDelete(model));
        genFile("ControllerTemplate.ftl", model, extra, targetFile);
        log.info("成功生成Controller:{}", targetFile.toString());
    }

    public void genService(GenCode model) throws IOException, TemplateException {
        Path targetFile = StringUtils.isNotBlank(model.getGenPackage()) ?
                Paths.get(model.getJavaPath(), "service", model.getGenPackage(), model.getClassName() + "Service.java")
                        .toAbsolutePath() :
                Paths.get(model.getJavaPath(), "service", model.getClassName() + "Service.java").toAbsolutePath();
        Map<String, Object> extra = new HashMap<>();
        extra.put("softDelete", canSoftDelete(model));
        genFile("ServiceTemplate.ftl", model, extra, targetFile);
        log.info("成功生成Service:{}", targetFile.toString());
    }

    public void genRepo(GenCode model) throws IOException, TemplateException, ClassNotFoundException {
        Path targetFile = StringUtils.isNotBlank(model.getGenPackage()) ?
                Paths.get(model.getJavaPath(), "repo", model.getGenPackage(), model.getClassName() + "Repo.java")
                        .toAbsolutePath() :
                Paths.get(model.getJavaPath(), "repo", model.getClassName() + "Repo.java").toAbsolutePath();
        Map<String, Object> extra = new HashMap<>();
        extra.put("softDelete", canSoftDelete(model));
        genFile("RepoTemplate.ftl", model, extra, targetFile);
        log.info("成功生成Repo:{}", targetFile.toString());
    }

    public void genListView(GenCode model) throws IOException, TemplateException {
        Path targetFile = Paths.get(model.getViewPath(), model.getClassName() + "List.vue").toAbsolutePath();
        Map<String, Object> extra = new HashMap<>();
        extra.put("softDelete", canSoftDelete(model));
        genFile("ListViewTemplate.ftl", model, extra, targetFile);
        log.info("成功生成ListView:{}", targetFile.toString());
    }

    public void genEditView(GenCode model) throws IOException, TemplateException, FontFormatException {
        Path targetFile = Paths.get(model.getViewPath(), model.getClassName() + "Edit.vue").toAbsolutePath();
        Map<String, Object> map = new HashMap<>();
        int maxLabelWidth = 0;
        for (TableField field : model.getFields()) {
            String label = StringUtils.isEmpty(field.getRemark()) ? field.getModelName() : field.getRemark();
            int labelWidth = measureText(label, 14);
            if (labelWidth > maxLabelWidth) {
                maxLabelWidth = labelWidth;
            }
        }
        map.put("labelWidth", maxLabelWidth + 25 + "px");
        map.put("softDelete", canSoftDelete(model));
        genFile("EditViewTemplate.ftl", model, map, targetFile);
        log.info("成功生成EditView:{}", targetFile.toString());
    }

    private void genFile(String template, GenCode model, Map<String, Object> extraData, Path path) throws IOException, TemplateException {
        Map<String, Object> data = new HashMap<>();
        data.put("model", model);
        if (extraData != null) {
            data.putAll(extraData);
        }

        Files.createDirectories(path.getParent());

        Template temp = cfg.getTemplate(template);
        Writer out = new FileWriter(path.toString());
        temp.process(data, out);
        out.flush();
        out.close();
    }

    private int measureText(String text, float fontSize) throws IOException, FontFormatException {
        AffineTransform affinetransform = new AffineTransform();
        FontRenderContext frc = new FontRenderContext(affinetransform, true, true);
        Font font = Font.createFont(Font.TRUETYPE_FONT, this.getClass()
                .getResourceAsStream("/font/SourceHanSansCN-Normal.ttf"));
        return (int) (font.deriveFont(fontSize).getStringBounds(text, frc).getWidth());
    }

    public void genRouter(GenCode model) {
        try {
            File file = new File(model.getRouterPath(), "router.js");
            BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
            StringBuilder routerJs = new StringBuilder();
            String line = null;
            while ((line = reader.readLine()) != null) {
                routerJs.append(line).append("\n");
            }
            reader.close();

            int insertLocation = routerJs.indexOf("/**INSERT_LOCATION**/");
            if (insertLocation > -1) {
                String remark = model.getRemark();
                String routeName = StringUtils.capitalize(model.getClassName());
                String routePath = StringUtils.uncapitalize(model.getClassName());
                String route = "{\n                    path: '/"
                        + routePath
                        + "Edit',\n                    name: '"
                        + routeName
                        + "Edit',\n                    component: () => import(/* webpackChunkName: \"" + routePath + "Edit\" */ '@/views/"
                        + routeName
                        + "Edit.vue'),\n                    meta: {\n                       title: '" + remark + "编辑',\n"
                        + "                    },\n                },\n                {\n                    path: '/"
                        + routePath
                        + "List',\n                    name: '"
                        + routeName
                        + "List',\n                    component: () => import(/* webpackChunkName: \"" + routePath + "List\" */ '@/views/"
                        + routeName
                        + "List.vue'),\n                    meta: {\n                       title: '" + remark + "',\n"
                        + "                    },\n               }\n                ";
                boolean needComma = !routerJs.toString().substring(0, insertLocation).trim().endsWith(",");
                if (needComma) {
                    routerJs.insert(routerJs.toString().substring(0, insertLocation).lastIndexOf("}") + 1, ",");
                    insertLocation++;
                }
                routerJs.insert(insertLocation, route);
            }

            BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(file));
            bufferedOutputStream.write(routerJs.toString().trim().getBytes());
            bufferedOutputStream.close();

            System.out.println("成功生成路由:" + file.getAbsolutePath());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private boolean canSoftDelete(GenCode model) {
        return true;
    }
}
